using System;
using System.Collections.Specialized;
using System.IO;
using System.Drawing;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Security;

namespace com.flajaxian
{
    /// <summary>
    /// Flajaxian FileUploader is .NET web control designed for asynchronous file upload 
    /// of multiple files at the same time, without a page post back and with a 
    /// progress bar indicating the current upload progress. 
    /// The control can be extended by the addition of server side and client side adapters. 
    /// Each adapter has to extend the abstract class <b>com.flajaxian.FileUploaderAdapter</b>
    /// </summary>
    [PersistChildren(false), ParseChildren(true), ToolboxBitmap(typeof(ObjectDataSource)), AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class FileUploader : WebControl 
    {
        /// <summary>
        /// The FileReceived event is called right after the processing of all the adapters.
        /// </summary>
        public event EventHandler<FileReceivedEventArgs> FileReceived;

        private readonly FileUploaderAdapterCollection _adapters;
        private bool _registerAspSessionCookies = true;
        private Dictionary<string, string> _state;

        private string _maxFileQueueSizeString;
        private double _maxFileQueueSize = Double.MinValue;
        private string _maxFileSizeString;
        private double _maxFileSize = Double.MinValue;
        private int _maxNumberFiles = Int32.MinValue;
        private bool _suppressQueryStringParametersOnUploadUrl;

        private string _maxFileNumberReachedMessage;
        private string _maxFileQueueSizeReachedMessage;
        private string _maxFileSizeReachedMessage;

        private string _javascriptID;
        private string _allowedFileTypes;
        private string _cancelledSuffix;
        private string _externalSwfObjectJsPath;
        private string _uploadDataFieldName;
        private bool _isDebug;
        private string _pageUrl;
        private string _cssUrl;
        private string _closeBtnUrl;
        private string _closedArrowUrl;
        private string _openedArrowUrl;
        private bool _clearListWhenQueueEnds = true;
        private bool _requestAsPostBack;
        private bool _uploadRequiresJsConfirmation;
        private bool _initiallyInvisible;
        private string _flashAttributes;
        private bool _transparentBackground;
        private bool _useInsideUpdatePanel;
        private bool _appendToUrlFileIndex = true;

        private string _jsFunc_GenerateBaseHtml;
        private string _jsFunc_GenerateFileRow;
        private string _jsFunc_DisposeFileRow;
        private string _jsFunc_FileStateChanged;
        private string _jsFunc_PercentageChanged;
        private string _jsFunc_PositionFilesList;
        private string _jsFunc_RenderFilesList;
        private string _jsFunc_Init;
        private string _jsFunc_Dispose;

        private string _imagesPath;

        private string _browseBtnUrl;
        private string _browseBtnOverUrl;
        private string _browseBtnPressUrl;
        private string _browseBtnDisableUrl;

        private string _uploadBtnUrl;
        private string _uploadBtnOverUrl;
        private string _uploadBtnPressUrl;
        private string _uploadBtnDisableUrl;

        private string _cancelBtnUrl;
        private string _cancelBtnOverUrl;
        private string _cancelBtnPressUrl;
        private string _cancelBtnDisableUrl;

        private string _progressBackUrl;
        private string _progressForeUrl;

        private int _browseBtnX = Int32.MinValue;
        private int _browseBtnY = Int32.MinValue;
        private int _browseBtnWidth = Int32.MinValue;
        private int _browseBtnHeight = Int32.MinValue;

        private int _uploadBtnX = Int32.MinValue;
        private int _uploadBtnY = Int32.MinValue;
        private int _uploadBtnWidth = Int32.MinValue;
        private int _uploadBtnHeight = Int32.MinValue;

        private int _cancelBtnX = Int32.MinValue;
        private int _cancelBtnY = Int32.MinValue;
        private int _cancelBtnWidth = Int32.MinValue;
        private int _cancelBtnHeight = Int32.MinValue;

        private int _progressX = Int32.MinValue;
        private int _progressY = Int32.MinValue;
        private int _progressWidth = Int32.MinValue;
        private int _progressHeight = Int32.MinValue;

        private Color _progressBorderColor = Constants.DefaultProgressBarBorderColor;
        private int _progressBorderSize = Int32.MinValue;
        private double _progressBorderAlpha = Double.MinValue;

        /// <summary>
        /// the constructor has no arguments
        /// </summary>
        public FileUploader()
        {
            this._adapters = new FileUploaderAdapterCollection();
            this.Adapters.Parent = this;

            this.MaxFileSize = "999GB";
            this.MaxFileQueueSize = "999GB";
            this.MaxNumberFiles = Int32.MaxValue;
        }

        #region Public Static Methods

        /// <summary>
        /// A method must be called in Global.asax Application_BeginRequest method 
        /// in order to fix flash debug player bug of not transfering the cookies with the upload request.
        /// 
        /// This is an example of how to add it:
        /// <example>
        /// void Application_BeginRequest(object sender, EventArgs e){
        ///     com.flajaxian.FileUploader.RegisterAspCookies();
        /// }
        /// </example>
        /// </summary>
        public static void RegisterAspCookies()
        {
            // checks if this is a file upload request
            if (HttpContext.Current.Request.Files.Count > 0)
            {
                RegisterSingleAspCookie(Constants.Key_AspCookie_SessionState, Constants.Key_AspCookie_Session);
                RegisterSingleAspCookie(Constants.Key_AspCookie_AuthState, Constants.Key_AspCookie_Auth);
            }
        }

        /// <summary>
        /// Converts a string of the type 1KB or 30mb into bytes
        /// </summary>
        /// <param name="size">A string of the type 1KB or 30mb</param>
        /// <returns>bytes</returns>
        public static double ParseBytes(string size)
        {
            if (size == null) return (long)-1;
            size = size.ToUpper().Trim();
            double bytes = 0;
            string[] suffixes = new string[] { "BYTES", "KB", "K", "MB", "M", "GB", "G", "TB", "T" };
            long[] multiplyBy = new long[] { 1, (1 << 10), (1 << 10), (1 << 20), (1 << 20), (1 << 30), (1 << 30), ((Int64)1 << 40), ((Int64)1 << 40) };

            for (int i = 0; i < suffixes.Length; i++)
            {
                string suffix = suffixes[i];
                if (size.EndsWith(suffix))
                {
                    bytes = ParseDouble(size.Replace(suffix, String.Empty), multiplyBy[i]);
                    return bytes;
                }
            }

            bytes = ParseDouble(size, 1);

            return bytes;
        }
        /// <summary>
        /// Transforms Color object into string of a type "#FFA127"
        /// </summary>
        public static string ColorToHtml(Color c)
        {
            return ("#" + c.R.ToString("X2", null) + c.G.ToString("X2", null) + c.B.ToString("X2", null));
        }
        #endregion

        #region Private Static Methods
        private static double ParseDouble(string size, long multiplyBy)
        {
            try
            {
                return Math.Round(Double.Parse(size.Trim()) * multiplyBy, 0);
            }
            catch (FormatException)
            {
                return (long)-1;
            }
        }
        private static string EscapeQuotes(object value)
        {
            if (value == null) return null;
            string str = value.ToString();
            if (str == String.Empty) return str;
            return str.Replace(@"""", @"\""").Replace("&", "_(!AMP!)_");
        }
        #endregion

        #region Private Instance Methods

        private static void RegisterSingleAspCookie(string stateID, string cookieName)
        {
            string sessCookie = HttpContext.Current.Request.QueryString[stateID];
            if (sessCookie != null)
            {
                HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(cookieName);
                if (cookie == null)
                {
                    cookie = new HttpCookie(cookieName);
                    HttpContext.Current.Request.Cookies.Add(cookie);
                }
                cookie.Value = sessCookie;
                HttpContext.Current.Request.Cookies.Set(cookie);
            }
        }

        private string ComposePageUrl(string pageUrl)
        {
            if(this.SuppressQueryStringParametersOnUploadUrl)
            {
                return pageUrl;
            }
            StringBuilder sb = new StringBuilder(pageUrl);
            string idTemplate = null;
            if(!pageUrl.Contains("?"))
            {
                idTemplate = "?__ID={0}&";
            }
            else if (pageUrl.EndsWith("?") || pageUrl.EndsWith("&"))
            {
                idTemplate = "__ID={0}&";
            }
            else
            {
                idTemplate = "&__ID={0}&";
            }
            sb.AppendFormat(idTemplate, this.ClientID);
            if(this.RegisterAspSessionCookies)
            {
                HttpCookie sessCookie = this.Page.Request.Cookies[Constants.Key_AspCookie_Session];
                if (sessCookie != null)
                {
                    sb.AppendFormat("{0}={1}&", Constants.Key_AspCookie_SessionState, sessCookie.Value);
                }
                HttpCookie authCookie = this.Page.Request.Cookies[Constants.Key_AspCookie_Auth];
                if (authCookie != null)
                {
                    sb.AppendFormat("{0}={1}&", Constants.Key_AspCookie_AuthState, authCookie.Value);
                }
            }
            sb.Replace("&", "_(!AMP!)_");
            return sb.ToString();
        }

        /// <summary>
        /// Client side code is registered on PreRenderComplete 
        /// </summary>
        private void Page_PreRenderComplete(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(this.ExternalSwfObjectJsPath))
            {
                this.Page.ClientScript.RegisterClientScriptResource(this.GetType(), 
                    this.IsDebug ? Constants.ResourcePath_SwfObjectDebug : Constants.ResourcePath_SwfObject);
            }
            else
            {
                this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), Constants.IncludeKey_SwfObject, this.ResolveClientUrl(this.ExternalSwfObjectJsPath));
            }
            this.Page.ClientScript.RegisterClientScriptResource(this.GetType(), Constants.ResourcePath_FileUploader2);
            if (this.CssUrl != String.Empty)
            {
                string link = String.Format(Constants.LinkTagTemplate, this.CssUrl);
                bool register = true;
                if(this.Page.Header != null)
                {
                    foreach (Control c in this.Page.Header.Controls)
                    {
                        LiteralControl lc = c as LiteralControl;
                        if(lc != null && lc.Text == link)
                        {
                            register = false;
                            break;
                        }
                    }
                    if(register)
                    {
                        this.Page.Header.Controls.Add(new LiteralControl(link));
                    }
                }
                else
                {
                    this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), this.ClientID, link, false);
                }
            }
        }





        private void ProcessFiles(HttpFileCollection files)
        {
            foreach (string key in files.AllKeys)
            {
                HttpPostedFile file = files[key];
                for (int i = 0; i < this.Adapters.Count; i++)
                {
                    this.Adapters[i].ProcessFile(file);
                }
                bool isLast = false;
                Boolean.TryParse(this.Context.Request.QueryString["__isLast"], out isLast);
                int index = 0;
                Int32.TryParse(this.Context.Request.QueryString["__index"], out index);

                this.OnFileReceived(new FileReceivedEventArgs(file, isLast, index));
            }
        }

        private static void AppendIfDoesNotEqual<T>(StringBuilder sb, string propName, T value, T notSuch) where T : IEquatable<T>
        {
            Type t = typeof(T);
            if(t == typeof(Boolean))
            {
                if (value.ToString().ToLower() != notSuch.ToString().ToLower())
                {
                    Append(sb, propName, value.ToString().ToLower(), "");
                }
                return;
            }
            if (t.IsClass && value == null && notSuch == null)
            {
                return;
            }
            if (!value.Equals(notSuch))
            {
                Append(sb, propName, EscapeQuotes(value), (t == typeof(String)) ? @"""" : "");
            }
        }

        private static void Append(StringBuilder sb, string propName, string value, string quote)
        {
            if (sb.Length > 0 && sb[sb.Length - 1] != ',') sb.Append(',');
            sb.AppendFormat(@"{0}:{2}{1}{2}", propName, EscapeQuotes(value), quote);
        }

        private string PropertiesToFlashVarJson()
        {
            StringBuilder fvSb = new StringBuilder();
            // required variables
            Append(fvSb, "id", EscapeQuotes(this.ClientID), @"""");
            Append(fvSb, "targetUrl", this.PageUrl, @"""");
            Append(fvSb, "debug", this.IsDebug.ToString().ToLower(), "");
            Append(fvSb, "enabled", this.Enabled.ToString().ToLower(), "");
            Append(fvSb, "isIE", "Flajaxian.isIE()", "");
            Append(fvSb, "clearListAtEnd", this.ClearListWhenQueueEnds.ToString().ToLower(), "");
            Append(fvSb, "uploadRequiresJsConfirmation", this.UploadRequiresJsConfirmation.ToString().ToLower(), "");
            Append(fvSb, "initiallyInvisible", this.InitiallyInvisible.ToString().ToLower(), "");
            // optional variables
            AppendIfDoesNotEqual(fvSb, "appendToUrlFileIndex", this.AppendToUrlFileIndex, true);
            AppendIfDoesNotEqual(fvSb, "uploadDataFieldName", this.UploadDataFieldName, null);
            AppendIfDoesNotEqual(fvSb, "allowedFileTypes", this.AllowedFileTypes, null);
            AppendIfDoesNotEqual(fvSb, "maxFileSize", this._maxFileSize, Double.MinValue);
            AppendIfDoesNotEqual(fvSb, "maxFileQueueSize", this._maxFileQueueSize, Double.MinValue);
            AppendIfDoesNotEqual(fvSb, "maxFileNumberLimit", this.MaxNumberFiles, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "imagesPath", this.ImagesPath, null);
            AppendIfDoesNotEqual(fvSb, "browseBtnUrl", this.BrowseButtonUrl, null);
            AppendIfDoesNotEqual(fvSb, "browseBtnOverUrl", this.BrowseButtonOverUrl, null);
            AppendIfDoesNotEqual(fvSb, "browseBtnPressUrl", this.BrowseButtonPressedUrl, null);
            AppendIfDoesNotEqual(fvSb, "browseBtnDisableUrl", this.BrowseButtonDisabledUrl, null);
            AppendIfDoesNotEqual(fvSb, "uploadBtnUrl", this.UploadButtonUrl, null);
            AppendIfDoesNotEqual(fvSb, "uploadBtnOverUrl", this.UploadButtonOverUrl, null);
            AppendIfDoesNotEqual(fvSb, "uploadBtnPressUrl", this.UploadButtonPressedUrl, null);
            AppendIfDoesNotEqual(fvSb, "uploadBtnDisableUrl", this.UploadButtonDisabledUrl, null);
            AppendIfDoesNotEqual(fvSb, "cancelBtnUrl", this.CancelButtonUrl, null);
            AppendIfDoesNotEqual(fvSb, "cancelBtnOverUrl", this.CancelButtonOverUrl, null);
            AppendIfDoesNotEqual(fvSb, "cancelBtnPressUrl", this.CancelButtonPressedUrl, null);
            AppendIfDoesNotEqual(fvSb, "cancelBtnDisableUrl", this.CancelButtonDisabledUrl, null);
            AppendIfDoesNotEqual(fvSb, "progressBackUrl", this.ProgressBackUrl, null);
            AppendIfDoesNotEqual(fvSb, "progressForeUrl", this.ProgressForeUrl, null);
            AppendIfDoesNotEqual(fvSb, "browseBtnX", this.BrowseButtonX, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "browseBtnY", this.BrowseButtonY, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "browseBtnWidth", this.BrowseButtonWidth, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "browseBtnHeight", this.BrowseButtonHeight, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "uploadBtnX", this.UploadButtonX, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "uploadBtnY", this.UploadButtonY, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "uploadBtnWidth", this.UploadButtonWidth, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "uploadBtnHeight", this.UploadButtonHeight, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "cancelBtnX", this.CancelButtonX, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "cancelBtnY", this.CancelButtonY, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "cancelBtnWidth", this.CancelButtonWidth, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "cancelBtnHeight", this.CancelButtonHeight, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "progressX", this.ProgressBarX, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "progressY", this.ProgressBarY, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "progressWidth", this.ProgressBarWidth, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "progressHeight", this.ProgressBarHeight, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "progressBorderColor", this.ProgressBorderColor.ToArgb(), Constants.DefaultProgressBarBorderColor.ToArgb());
            AppendIfDoesNotEqual(fvSb, "progressBorderSize", this.ProgressBorderSize, Int32.MinValue);
            AppendIfDoesNotEqual(fvSb, "progressBorderAlpha", this.ProgressBorderAlpha, Double.MinValue);
            // append the state
            foreach(KeyValuePair<String, string> pair in this.State)
            {
                Append(fvSb, Constants.StatePrefix + pair.Key, EscapeQuotes(pair.Value), @"""");
            }
            return fvSb.ToString();
        }


        

        private void WriteInitialization(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.AddAttribute(HtmlTextWriterAttribute.Href, "http://get.adobe.com/flashplayer/");
            writer.AddAttribute(HtmlTextWriterAttribute.Target, "_blank");
            writer.RenderBeginTag(HtmlTextWriterTag.A);
            writer.Write(SR.PleaseInstallAdobeFlashPlayer);
            writer.RenderEndTag();
            writer.RenderEndTag();
            writer.WriteLine();
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
            writer.AddAttribute("language", "javascript");
            writer.RenderBeginTag(HtmlTextWriterTag.Script);
            for (int i = 0; i < this.Adapters.Count; i++)
            {
                this.Adapters[i].WriteJavaScriptInstantiation(writer);
                if (this.Adapters[i].UploadRequiresJsConfirmation && !this.UploadRequiresJsConfirmation)
                {
                    this.UploadRequiresJsConfirmation = true;
                }
            }
            writer.Write("{0} = new Flajaxian.FileUploader2('{1}');", this.JavascriptID, this.ClientID);
            writer.WriteLine();
            writer.Write("function {0}_MarkInitialized(){{ {1}.markInitialized(); }}", this.ClientID, this.JavascriptID);
            writer.WriteLine();
            writer.Write("function {0}_SetFileList(list){{ {1}.setFileList(list); }}", this.ClientID, this.JavascriptID);
            writer.WriteLine();
            writer.Write("function {0}_SetUploadProgress(evt){{ {1}.setUploadProgress(evt); }}", this.ClientID, this.JavascriptID);
            writer.WriteLine();
            writer.Write("function {0}_SetChangedFileStates(list){{ {1}.setChangedFileStates(list); }}", this.ClientID, this.JavascriptID);
            writer.WriteLine();
            writer.Write("function {0}_LimitReached(type){{ {1}.limitReached(type); }}", this.ClientID, this.JavascriptID);
            writer.WriteLine();
            writer.Write("function {0}_ConfirmUpload(){{ {1}.confirmUpload(); }}", this.ClientID, this.JavascriptID);
            writer.WriteLine();
            writer.Write("function {0}_Dispose(){{ {1}.dispose(); }}", this.ClientID, this.JavascriptID);
            writer.WriteLine();
            writer.Write(@"function {0}_EmbedFlash(){{", this.ClientID);
            writer.Indent++;
            writer.WriteLine();
            writer.Write(@"var p = {{menu:false,base:'.',scale:'noscale',bgcolor:'{0}'{1}}};", 
                ColorToHtml(this.BackColor), this.TransparentBackground ? ",wmode:'transparent'" : String.Empty);
            writer.WriteLine();
            writer.Write(@"var a = {{{0}}};", this.FlashAttributes ?? String.Empty);
            writer.WriteLine();
            writer.Write(@"var fv = {"); 
            writer.Write(this.PropertiesToFlashVarJson());
            writer.Write(@"};");
            writer.WriteLine();
            writer.Write(@"swfobject.embedSWF(""{0}"", ""{1}"", ""{2}"", ""{3}"", ""9.0.0"", ""{4}"", fv, p, a);", 
                                this.FlashUrl, this.ClientID, this.Width.Value, this.Height.Value, this.ExpressInstallUrl);
            writer.WriteLine();
            writer.Write(@"var fu = {0};", this.JavascriptID);
            writer.WriteLine();
            WriteJsSetter(writer, "set_generateBaseHtmlFunc", this.JsFunc_GenerateBaseHtml ?? "Flajaxian.generateBaseHtml", false);
            WriteJsSetter(writer, "set_generateFileRowFunc", this.JsFunc_GenerateFileRow ?? "Flajaxian.generateFileRow", false);
            WriteJsSetter(writer, "set_disposeFileRowFunc", this.JsFunc_DisposeFileRow ?? "Flajaxian.disposeFileRow", false);
            WriteJsSetter(writer, "set_fileStateChangedFunc", this.JsFunc_FileStateChanged ?? "Flajaxian.fileStateChanged", false);
            WriteJsSetter(writer, "set_percentageChangedFunc", this.JsFunc_PercentageChanged ?? "Flajaxian.percentageChangedFunc", false);
            WriteJsSetter(writer, "set_maxFileNumberReachedFunc", this.JsFunc_PercentageChanged ?? "Flajaxian.maxFileNumberReached", false);
            WriteJsSetter(writer, "set_maxFileSizeReachedFunc", this.JsFunc_PercentageChanged ?? "Flajaxian.maxFileSizeReached", false);
            WriteJsSetter(writer, "set_maxQueueSizeReachedFunc", this.JsFunc_PercentageChanged ?? "Flajaxian.maxQueueSizeReached", false);
            WriteJsSetter(writer, "set_positionFilesListFunc", this.JsFunc_PositionFilesList ?? "Flajaxian.positionFilesList", false);
            WriteJsSetter(writer, "set_renderFilesListFunc", this.JsFunc_RenderFilesList ?? "Flajaxian.renderFilesList", false);

            WriteJsSetter(writer, "set_strings", this.GetStringsJson(), false);
            WriteJsSetter(writer, "set_openedArrowUrl", this.OpenedArrowUrl, true);
            WriteJsSetter(writer, "set_closedArrowUrl", this.ClosedArrowUrl, true);
            WriteJsSetter(writer, "set_closeBtnUrl", this.CloseButtonUrl, true);
            WriteJsSetter(writer, "set_flashWidth", this.Width.Value.ToString(), false);
            WriteJsSetter(writer, "set_flashHeight", this.Height.Value.ToString(), false);
            WriteJsSetter(writer, "set_uploadRequiresJsConfirmation", this.UploadRequiresJsConfirmation.ToString().ToLower(), false);
            WriteJsSetter(writer, "set_clearListAtEnd", this.ClearListWhenQueueEnds.ToString().ToLower(), false);

            for (int i = 0; i < this.Adapters.Count; i++)
            {
                this.Adapters[i].WritePreInitializationJavaScript(writer);
            }
            if(this.RequestAsPostBack)
            {
                writer.Write("fu.addUploadPreProcessor(Flajaxian.requestAsPostBack);");
            }
            writer.Write("fu.initialize();");
            writer.WriteLine();
            writer.Indent--;
            writer.Write(@"}");
            writer.WriteLine();

            if (!String.IsNullOrEmpty(this.JsFunc_Dispose))
            {
                writer.Write(@"{1} = {0}_Dispose;", this.ClientID, this.JsFunc_Dispose);
            }

            if (!String.IsNullOrEmpty(this.JsFunc_Init))
            {
                writer.Write(@"{1} = {0}_EmbedFlash;", this.ClientID, this.JsFunc_Init);
            }
            else if(this.UseInsideUpdatePanel)
            {
                writer.Write(@"if(typeof(Sys) != 'undefined' && typeof(Sys.WebForms) != 'undefined' && typeof(Sys.WebForms.PageRequestManager) != 'undefined'){ ");
                writer.Write(@"Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded( {0}_EmbedFlash ); ", this.ClientID);
                writer.Write(@"Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest( {0}_Dispose ); ", this.ClientID);
                writer.Write(@"}}else{{ {0}_EmbedFlash(); }}", this.ClientID);
            }
            else
            {
                writer.Write(@"{0}_EmbedFlash();", this.ClientID);
            }
            writer.Indent--;
            writer.WriteLine();
            writer.RenderEndTag();
        }

        private void WriteJsSetter(HtmlTextWriter writer, string methodName, string value, bool quote)
        {
            if (quote) writer.Write(@"fu.{1}(""{0}"");", value.Replace(@"""", @"\"""), methodName);
            else writer.Write("fu.{1}({0});", value, methodName);
            writer.WriteLine();
        }

        private string GetStringsJson()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("{");
            sb.AppendFormat("maxFileNumberReached:'{0}'", String.Format(this.MaxFileNumberReachedMessage, this.MaxNumberFiles).Replace(@"'",@"\'"));
            sb.Append(",");
            sb.AppendFormat("maxFileSizeReached:'{0}'", String.Format(this.MaxFileSizeReachedMessage, this.MaxFileSize).Replace(@"'", @"\'"));
            sb.Append(",");
            sb.AppendFormat("maxQueueSizeReached:'{0}'", String.Format(this.MaxFileQueueSizeReachedMessage, this.MaxFileQueueSize).Replace(@"'", @"\'"));
            sb.Append("}");
            return sb.ToString();
        }

        #endregion


        #region Overriden Properties and Methods

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (this.Width.IsEmpty)
            {
                this.Width = Constants.DefaultWidth;
            }
            if (this.Height.IsEmpty)
            {
                this.Height = Constants.DefaultHeight;
            }
            if(this.BackColor.IsEmpty)
            {
                this.BackColor = Constants.DefaultBackColor;
            }
        }

        /// <summary>
        /// Files are being processes in OnLoad stage.
        /// </summary>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.Page.PreRenderComplete += this.Page_PreRenderComplete;
            if (this.FileIsPosted)
            {
                this.ProcessFiles(this.Page.Request.Files);
                this.Page.Response.End();
     
            }
        }

        /// <summary>
        /// Writes JavaScript object initialization code and the HTML
        /// </summary>
        protected override void Render(HtmlTextWriter writer)
        {
            this.Page.VerifyRenderingInServerForm(this);
            this.WriteInitialization(writer);
        }

        #endregion

        #region Protected Virtual Methods
        /// <summary>
        /// The FileReceived event is called right after the processing of all the adapters.
        /// </summary>
        /// <param name="args">Event arguments with a reference to the uploaded HttpPostedFile object</param>
        protected virtual void OnFileReceived(FileReceivedEventArgs args)
        {
            if (this.FileReceived != null)
            {
                this.FileReceived(this, args);
            }
        }
        #endregion


        #region Read-Only Properties
        /// <summary>
        /// Indicates if the current request is file upload from Flash
        /// </summary>
        public bool FileIsPosted
        {
            get
            {
                string id = this.Page.Request.QueryString["__ID"];
                return (
                        id != null &&
                        id == this.ClientID &&
                        this.Page.Request.Files.Count > 0
                        );
            }
        }

        /// <summary>
        /// A collection of adapters, each adapter can perform server side tasks and can override client side code. (read only)
        /// Each adapter must extend the abstract class
        /// <b>com.flajaxian.FileUploaderAdapter</b>
        /// </summary>
        [MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty), DefaultValue((string)null)]
        public FileUploaderAdapterCollection Adapters
        {
            get { return this._adapters; }
        }


        /// <summary>
        /// State information that can be passed with the file transfer
        /// </summary>
        /// <example>
        /// FileUploader1.State.Add("MyKey", "MyValue");
        /// 
        /// if(FileUploader1.FileIsPosted){
        ///     string stateInfo = this.Request.QueryString["MyKey"];
        /// }
        /// </example>
        public Dictionary<string, string> State
        {
            get
            {
                if (this._state == null)
                {
                    this._state = new Dictionary<string, string>();
                }
                return this._state;
            }
        }


        /// <summary>
        /// The ID used for the javascript variable holding the instence of the Flajaxian.FileUploader object (read only)
        /// </summary>
        public string JavascriptID
        {
            get
            {
                if (!String.IsNullOrEmpty(this.ClientID))
                {
                    this._javascriptID = String.Concat(Constants.JS_WindowPrefix, this.ClientID);
                }
                return this._javascriptID;
            }
        }
        /// <summary>
        /// Returns http or https depending on the current request. (read only)
        /// </summary>
        public string Protocol
        {
            get { return this.Page.Request.Url.Scheme; }
        }

        /// <summary>
        /// A URL of the swf file.
        /// </summary>
        protected string FlashUrl
        {
            get { return this.Page.ClientScript.GetWebResourceUrl(this.GetType(), Constants.ResourcePath_Flash)/* + "&r=" + this.GetRandomString()*/; }
        }
        
        /// <summary>
        /// ExpressInstall.swf SwfObject embeded file url
        /// </summary>
        protected string ExpressInstallUrl
        {
            get { return this.Page.ClientScript.GetWebResourceUrl(this.GetType(), Constants.ResourcePath_ExpressInstall); }
        }
            /// <summary>
        /// The property State transformed into string of a type key1=value1&key2=value2 and passed with the file load
        /// </summary>
        /*public string StateAsQueryString
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                this.DoRegisterAspSessionCookiesInState();
                foreach (KeyValuePair<string, string> pair in this.State)
                {
                    sb.AppendFormat("{0}={1}&", this.Page.Server.HtmlEncode(pair.Key), this.Page.Server.HtmlEncode(pair.Value));
                }
                return sb.ToString();
            }
        }*/
        #endregion

        #region Read-Write properties

        /// <summary>
        /// Transparency flash background flag
        /// </summary>
        public bool TransparentBackground
        {
            get { return this._transparentBackground; }
            set { this._transparentBackground = value; } 
        }


        /// <summary>
        /// Attributes passed to the SwfObject in a following format:
        /// FlashAttributes="style:'float:left',align:'left'"
        /// </summary>
        public string FlashAttributes
        {
            get { return this._flashAttributes; }
            set { this._flashAttributes = value; } 
        }

        /// <summary>
        /// If set makes the flash UI initially invisible
        /// </summary>
        public bool InitiallyInvisible
        {
            get { return this._initiallyInvisible; }
            set { this._initiallyInvisible = value; }
        }

        /// <summary>
        /// If set to true the control can be located inside an update panel
        /// </summary>
        public bool UseInsideUpdatePanel
        {
            get { return this._useInsideUpdatePanel; }
            set { this._useInsideUpdatePanel = value; }
        }

        /// <summary>
        /// A flag indicating weather all the form variables should be sent with the request.
        /// By default false.
        /// </summary>
        public bool RequestAsPostBack
        {
            get { return this._requestAsPostBack; }
            set
            {
                this._requestAsPostBack = value;
                if (this._requestAsPostBack && !this.UploadRequiresJsConfirmation)
                {
                    this.UploadRequiresJsConfirmation = true;
                }
            }
        }

        /// <summary>
        /// If set to true after user clicks on the Upload button upload is not initiated but instead
        /// a JavaScript processor passed to FileUploader.addUploadPreProcessor(function(uploader, array){ ... });
        /// is called and it must reinitiate the upload after
        /// </summary>
        public bool UploadRequiresJsConfirmation
        {
            get { return this._uploadRequiresJsConfirmation; }
            set
            {
                this._uploadRequiresJsConfirmation = value;
                if(!this.UploadRequiresJsConfirmation && this.RequestAsPostBack)
                {
                    throw new ApplicationException("You cannot set UploadRequiresJsConfirmation to 'false' while RequestAsPostBack is set to 'true'");
                }
            }
        }

        /// <summary>
        /// A flag indicating weather to clear the file list when the queue ends. 
        /// By default true.
        /// </summary>
        public bool ClearListWhenQueueEnds
        {
            get { return this._clearListWhenQueueEnds; }
            set { this._clearListWhenQueueEnds = value; }
        }

        /// <summary>
        /// Javascript function will be passed to jsObj.set_generateBaseHtmlFunc(func);
        /// </summary>
        public string JsFunc_GenerateBaseHtml
        {
            get { return this._jsFunc_GenerateBaseHtml; }
            set { this._jsFunc_GenerateBaseHtml = value; }
        }

        /// <summary>
        /// Javascript function will be passed to jsObj.set_generateFileRowFunc(func);
        /// </summary>
        public string JsFunc_GenerateFileRow
        {
            get { return this._jsFunc_GenerateFileRow; }
            set { this._jsFunc_GenerateFileRow = value; }
        }

        /// <summary>
        /// Javascript function will be passed to jsObj.set_disposeFileRowFunc(func);
        /// </summary>
        public string JsFunc_DisposeFileRow
        {
            get { return this._jsFunc_DisposeFileRow; }
            set { this._jsFunc_DisposeFileRow = value; }
        }

        /// <summary>
        /// Javascript function will be passed to jsObj.set_fileStateChangedFunc(func);
        /// </summary>
        public string JsFunc_FileStateChanged
        {
            get { return this._jsFunc_FileStateChanged; }
            set { this._jsFunc_FileStateChanged = value; }
        }

        /// <summary>
        /// Javascript function will be passed to jsObj.set_percentageChangedFunc(func);
        /// </summary>
        public string JsFunc_PercentageChanged
        {
            get { return this._jsFunc_PercentageChanged; }
            set { this._jsFunc_PercentageChanged = value; }
        }

        /// <summary>
        /// Javascript function will be passed to jsObj.set_positionFileListFunc(func);
        /// </summary>
        public string JsFunc_PositionFilesList
        {
            get { return this._jsFunc_PositionFilesList; }
            set { this._jsFunc_PositionFilesList = value; }
        }

        /// <summary>
        /// Javascript function will be passed to jsObj.set_renderFileListFunc(func);
        /// </summary>
        public string JsFunc_RenderFilesList
        {
            get { return this._jsFunc_RenderFilesList; }
            set { this._jsFunc_RenderFilesList = value; }
        }

        /// <summary>
        /// Javascript function. If set then the usual {ClientID}_EmbedFlash() will not be fired 
        /// so you have to call it yourself, for example:
        /// 
        /// JsFunc_Init="MyInitFunc"
        /// 
        /// then when you want to initialize it simply call MyInitFunc()
        /// 
        /// </summary>
        public string JsFunc_Init
        {
            get { return this._jsFunc_Init; }
            set { this._jsFunc_Init = value; }
        }

        /// <summary>
        /// Javascript function. Set as:
        /// 
        /// JsFunc_Dispose="MyDisposeFunc"
        /// 
        /// then when you want to dispose it simply call MyDisposeFunc()
        /// 
        /// </summary>
        public string JsFunc_Dispose
        {
            get { return this._jsFunc_Dispose; }
            set { this._jsFunc_Dispose = value; }
        }

        /// <summary>
        /// The URL of the page that will receive the file upload streams.
        /// </summary>
        public string PageUrl
        {
            get
            {
                if(this._pageUrl == null)
                {
                    return this.ComposePageUrl(this.Page.Request.Url.AbsoluteUri.Substring(0,
                            this.Page.Request.Url.AbsoluteUri.Length - this.Page.Request.Url.Query.Length));
                }
                return this.ComposePageUrl(this._pageUrl);
            }
            set { this._pageUrl = this.ResolveUrl(value); }
        }

        

        /// <summary>
        /// custom CSS file or by default Common.css embeded file url 
        /// </summary>
        public string CssUrl
        {
            get
            {
                if (this._cssUrl == null)
                {
                    return this.Page.ClientScript.GetWebResourceUrl(this.GetType(), Constants.ResourcePath_CommonCss);
                }
                return this._cssUrl;
            }
            set { this._cssUrl = this.ResolveUrl(value); }
        }

        /// <summary>
        /// url of the 'x' image to remove a chosen file 
        /// </summary>
        public string CloseButtonUrl
        {
            get
            {
                if (this._closeBtnUrl == null)
                {
                    return this.Page.ClientScript.GetWebResourceUrl(this.GetType(), Constants.ResourcePath_CloseButtonImage);
                }
                return this._closeBtnUrl;
            }
            set { this._closeBtnUrl = value; }
        }

        /// <summary>
        /// Closed state file box arrow image
        /// </summary>
        public string ClosedArrowUrl
        {
            get
            {
                if (this._closedArrowUrl == null)
                {
                    return this.Page.ClientScript.GetWebResourceUrl(this.GetType(), Constants.ResourcePath_ClosedArrowImage);
                }
                return this._closedArrowUrl;
            }
            set { this._closedArrowUrl = value; }
        }

        /// <summary>
        /// Opened state file box arrow image
        /// </summary>
        public string OpenedArrowUrl
        {
            get
            {
                if (this._openedArrowUrl == null)
                {
                    return this.Page.ClientScript.GetWebResourceUrl(this.GetType(), Constants.ResourcePath_OpenedArrowImage);
                }
                return this._openedArrowUrl;
            }
            set { this._openedArrowUrl = value; }
        }



        /// <summary>
        /// If set to true will pass asp session cookies in the state. By default set to true.
        /// </summary>
        public bool RegisterAspSessionCookies
        {
            get { return this._registerAspSessionCookies; }
            set { this._registerAspSessionCookies = value; }
        }

        /// <summary>
        /// If set to true upload url will have no query strings parameters added. By default set to false.
        /// </summary>
        public bool SuppressQueryStringParametersOnUploadUrl
        {
            get { return this._suppressQueryStringParametersOnUploadUrl; }
            set { this._suppressQueryStringParametersOnUploadUrl = value; }
        }
        

        
        /// <summary>
        /// Maximum allowed file size. The value can be passed with KB, MB, GB or TB prefixes, for example:
        /// "16MB" would mean 16 mega bytes
        /// </summary>
        public string MaxFileSize
        {
            get { return this._maxFileSizeString; }
            set
            {
                double size = ParseBytes(value);
                if (size < 0)
                {
                    throw new ApplicationException(String.Format(SR.ProblemParsingFileSize, value));
                }
                this._maxFileSizeString = value;
                this._maxFileSize = size;
            }
        }

        /// <summary>
        /// Maximum allowed number of files
        /// </summary>
        public int MaxNumberFiles
        {
            get { return this._maxNumberFiles; }
            set { this._maxNumberFiles = value; }
        }
        

        /// <summary>
        /// Maximum allowed file queue size. The value can be passed with KB, MB, GB or TB prefixes, for example:
        /// "16MB" would mean 16 mega bytes
        /// </summary>
        public string MaxFileQueueSize
        {
            get { return this._maxFileQueueSizeString; }
            set
            {
                double size = ParseBytes(value);
                if (size < 0)
                {
                    throw new ApplicationException(String.Format(SR.ProblemParsingFileQueueSize, value));
                }
                this._maxFileQueueSizeString = value;
                this._maxFileQueueSize = size;
            }
        }

        /// <summary>
        /// The message displayed when the maximum allowed number of files has been selected
        /// </summary>
        public string MaxFileNumberReachedMessage
        {
            get
            {
                if (this._maxFileNumberReachedMessage == null)
                {
                    return String.Format(SR.MaxFileNumberReached, this.MaxNumberFiles);
                }
                return String.Format(this._maxFileNumberReachedMessage, this.MaxNumberFiles);
            }
            set { this._maxFileNumberReachedMessage = value; }
        }

        /// <summary>
        /// The message displayed when the maximum allowed size for a single file has been reached
        /// </summary>
        public string MaxFileQueueSizeReachedMessage
        {
            get
            {
                if (this._maxFileQueueSizeReachedMessage == null)
                {
                    return String.Format(SR.MaxQueueSizeReached, this._maxFileQueueSizeString);
                }
                return String.Format(this._maxFileQueueSizeReachedMessage, this._maxFileQueueSizeString);
            }
            set { this._maxFileQueueSizeReachedMessage = value; }
        }

        /// <summary>
        /// The message displayed when the maximum allowed size for all the files in the queue has been reached
        /// </summary>
        public string MaxFileSizeReachedMessage
        {
            get
            {
                if (this._maxFileSizeReachedMessage == null)
                {
                    return String.Format(SR.MaxFileSizeReached, this._maxFileSizeString);
                }
                return String.Format(this._maxFileSizeReachedMessage, this._maxFileSizeString);
            }
            set { this._maxFileSizeReachedMessage = value; }
        }


        
        /// <summary>
        /// Suffix added to each file that has been cancelled by the user
        /// </summary>
        public string CancelledSuffix
        {
            get
            {
                if (this._cancelledSuffix == null)
                {
                    return SR.DefaultErrorSuffix;
                }
                return this._cancelledSuffix;
            }
            set { this._cancelledSuffix = value; }
        }
        

        /// <summary>
        /// If not specified all the file types are allowed for upload. 
        /// The format is as follow:
        /// File Group 1 Name:FileType1;FileType2;FileType3:MAC types|File Group 2 Name:FileType1;FileType2;FileType3:MAC types
        /// 
        /// for example if you want two file froups: "Image files" and "Flash Movies" you would use:
        /// AllowedFileTypes="Image Files:*.jpg;*.jpeg;*.gif;*.png:JPEG;jp2_;GIFf;PNGf|Flash Movies:*.swf:SWFL"
        /// 
        /// you can also omit the Mac types for example:
        /// AllowedFileTypes="Images:*.jpg;*.jpeg;*.gif;*.png"
        /// 
        /// if you need to use : or | characters in the description use them as doubled for example:
        /// AllowedFileTypes="Images:: ||*.jpg;*.jpeg;*.gif;*.png||:*.jpg;*.jpeg;*.gif;*.png"
        /// si your description will be
        /// Images: |*.jpg;*.jpeg;*.gif;*.png|
        /// </summary>
        public string AllowedFileTypes
        {
            get { return this._allowedFileTypes; }
            set
            {
                if (value == null || value.IndexOf(":") <= 0)
                {
                    throw new ApplicationException(SR.ProblemAllowedFileTypesWrongFormat);
                }
                this._allowedFileTypes = value;
            }
        }


        /// <summary>
        /// The path of the SwfObject JavaScript file
        /// </summary>
        public string ExternalSwfObjectJsPath
        {
            get { return this._externalSwfObjectJsPath; }
            set { this._externalSwfObjectJsPath = value; }
        }


        /// <summary>
        /// The name of the file stream request field, by default it is Filedata
        /// </summary>
        public string UploadDataFieldName
        {
            get { return this._uploadDataFieldName; }
            set { this._uploadDataFieldName = value; }
        }
        
        /// <summary>
        /// Appends __isLast and __index vars to query string true by default
        /// </summary>
        public bool AppendToUrlFileIndex
        {
            get { return this._appendToUrlFileIndex; }
            set { this._appendToUrlFileIndex = value; }
        }

        /// <summary>
        /// If set to true will emit debug console.log client calls
        /// </summary>
        public bool IsDebug
        {
            get { return this._isDebug; }
            set { this._isDebug = value; }
        }

        /// <summary>
        /// A path that will be used as path for all the images. 
        /// You can use it so you don't have to pass the same path for each image.
        /// </summary>
        public string ImagesPath
        {
            get { return this._imagesPath; }
            set { this._imagesPath = this.ResolveUrl(value); }
        }

        /// <summary>
        /// The path of the cancel button - normal state
        /// </summary>
        public string CancelButtonUrl
        {
            get { return this._cancelBtnUrl; }
            set { this._cancelBtnUrl = value; }
        }
        /// <summary>
        /// The path of the cancel button - over state
        /// </summary>
        public string CancelButtonOverUrl
        {
            get { return this._cancelBtnOverUrl; }
            set { this._cancelBtnOverUrl = value; }
        }
        /// <summary>
        /// The path of the cancel button - pressed state
        /// </summary>
        public string CancelButtonPressedUrl
        {
            get { return this._cancelBtnPressUrl; }
            set { this._cancelBtnPressUrl = value; }
        }
        /// <summary>
        /// The path of the cancel button - disabled state
        /// </summary>
        public string CancelButtonDisabledUrl
        {
            get { return this._cancelBtnDisableUrl; }
            set { this._cancelBtnDisableUrl = value; }
        }

        /// <summary>
        /// The path of the browse button - normal state
        /// </summary>
        public string BrowseButtonUrl
        {
            get { return this._browseBtnUrl; }
            set { this._browseBtnUrl = value; }
        }
        /// <summary>
        /// The path of the browse button - over state
        /// </summary>
        public string BrowseButtonOverUrl
        {
            get { return this._browseBtnOverUrl; }
            set { this._browseBtnOverUrl = value; }
        }
        /// <summary>
        /// The path of the browse button - pressed state
        /// </summary>
        public string BrowseButtonPressedUrl
        {
            get { return this._browseBtnPressUrl; }
            set { this._browseBtnPressUrl = value; }
        }
        /// <summary>
        /// The path of the browse button - disabled state
        /// </summary>
        public string BrowseButtonDisabledUrl
        {
            get { return this._browseBtnDisableUrl; }
            set { this._browseBtnDisableUrl = value; }
        }

        /// <summary>
        /// The path of the upload button - normal state
        /// </summary>
        public string UploadButtonUrl
        {
            get { return this._uploadBtnUrl; }
            set { this._uploadBtnUrl = value; }
        }
        /// <summary>
        /// The path of the upload button - over state
        /// </summary>
        public string UploadButtonOverUrl
        {
            get { return this._uploadBtnOverUrl; }
            set { this._uploadBtnOverUrl = value; }
        }
        /// <summary>
        /// The path of the upload button - pressed state
        /// </summary>
        public string UploadButtonPressedUrl
        {
            get { return this._uploadBtnPressUrl; }
            set { this._uploadBtnPressUrl = value; }
        }
        /// <summary>
        /// The path of the upload button - disabled state
        /// </summary>
        public string UploadButtonDisabledUrl
        {
            get { return this._uploadBtnDisableUrl; }
            set { this._uploadBtnDisableUrl = value; }
        }
        /// <summary>
        /// The path of the progress bar background
        /// </summary>
        public string ProgressBackUrl
        {
            get { return this._progressBackUrl; }
            set { this._progressBackUrl = value; }
        }
        /// <summary>
        /// The path of the progress bar foreground
        /// </summary>
        public string ProgressForeUrl
        {
            get { return this._progressForeUrl; }
            set { this._progressForeUrl = value; }
        }


        /// <summary>
        /// The X position of the browse button
        /// </summary>
        public int BrowseButtonX
        {
            get { return this._browseBtnX; }
            set { this._browseBtnX = value; }
        }
        /// <summary>
        /// The Y position of the browse button
        /// </summary>
        public int BrowseButtonY
        {
            get { return this._browseBtnY; }
            set { this._browseBtnY = value; }
        }
        /// <summary>
        /// The Width of the browse button
        /// </summary>
        public int BrowseButtonWidth
        {
            get { return this._browseBtnWidth; }
            set { this._browseBtnWidth = value; }
        }
        /// <summary>
        /// The Height of the browse button
        /// </summary>
        public int BrowseButtonHeight
        {
            get { return this._browseBtnHeight; }
            set { this._browseBtnHeight = value; }
        }

        /// <summary>
        /// The X position of the upload button
        /// </summary>
        public int UploadButtonX
        {
            get { return this._uploadBtnX; }
            set { this._uploadBtnX = value; }
        }
        /// <summary>
        /// The Y position of the upload button
        /// </summary>
        public int UploadButtonY
        {
            get { return this._uploadBtnY; }
            set { this._uploadBtnY = value; }
        }
        /// <summary>
        /// The Width of the upload button
        /// </summary>
        public int UploadButtonWidth
        {
            get { return this._uploadBtnWidth; }
            set { this._uploadBtnWidth = value; }
        }
        /// <summary>
        /// The Height of the upload button
        /// </summary>
        public int UploadButtonHeight
        {
            get { return this._uploadBtnHeight; }
            set { this._uploadBtnHeight = value; }
        }

        /// <summary>
        /// The X position of the cancel button
        /// </summary>
        public int CancelButtonX
        {
            get { return this._cancelBtnX; }
            set { this._cancelBtnX = value; }
        }
        /// <summary>
        /// The Y position of the cancel button
        /// </summary>
        public int CancelButtonY
        {
            get { return this._cancelBtnY; }
            set { this._cancelBtnY = value; }
        }
        /// <summary>
        /// The Width of the cancel button
        /// </summary>
        public int CancelButtonWidth
        {
            get { return this._cancelBtnWidth; }
            set { this._cancelBtnWidth = value; }
        }
        /// <summary>
        /// The Height of the cancel button
        /// </summary>
        public int CancelButtonHeight
        {
            get { return this._cancelBtnHeight; }
            set { this._cancelBtnHeight = value; }
        }

        /// <summary>
        /// The X position of the progress bar 
        /// </summary>
        public int ProgressBarX
        {
            get { return this._progressX; }
            set { this._progressX = value; }
        }
        /// <summary>
        /// The Y position of the progress bar 
        /// </summary>
        public int ProgressBarY
        {
            get { return this._progressY; }
            set { this._progressY = value; }
        }
        /// <summary>
        /// The Width of the progress bar 
        /// </summary>
        public int ProgressBarWidth
        {
            get { return this._progressWidth; }
            set { this._progressWidth = value; }
        }
        /// <summary>
        /// The Height of the progress bar 
        /// </summary>
        public int ProgressBarHeight
        {
            get { return this._progressHeight; }
            set { this._progressHeight = value; }
        }


        /// <summary>
        /// The Color of the progress bar border
        /// </summary>
        public Color ProgressBorderColor
        {
            get { return this._progressBorderColor; }
            set { this._progressBorderColor = value; }
        }
        /// <summary>
        /// The Color of the progress bar border
        /// </summary>
        public int ProgressBorderSize
        {
            get { return this._progressBorderSize; }
            set { this._progressBorderSize = value; }
        }
        /// <summary>
        /// The Alpha of the progress bar border
        /// </summary>
        public double ProgressBorderAlpha
        {
            get { return this._progressBorderAlpha; }
            set
            {
                if(value < 0 || value > 1)
                {
                    throw new ArgumentException(SR.ProgressBorderAlphaMustBeFrom0To1);
                }
                this._progressBorderAlpha = value;
            }
        }
        #endregion
    }

}
